﻿using SoftwareConsulting.BI11.NumericalMethods.Interfaces;
using SoftwareConsulting.BI11.NumericalMethods.Statistics;

namespace SoftwareConsulting.BI11.NumericalMethods.Statistics
{    
    /// <summary>
    /// Класс конструирования функции из функции плотности для заданной нормы
    /// </summary>
    public class ScaledProbabilityDensityFunction : IParametrizedOneVariableFunction
    {	    
        private double _binsCount;//число интервалов гистограммы
        private double _binWidth;//ширина интервала гистограммы	    
        private ProbabilityDensityFunction _probabilityDensity;//плотность распределения

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="probabilityDensityFunction">плотность распределения</param>
        /// <param name="binsCount">число интервалов гистограммы</param>
        /// <param name="binWidth">ширина интервала гистограммы</param>
        public ScaledProbabilityDensityFunction(ProbabilityDensityFunction probabilityDensityFunction, long binsCount, double binWidth)
        {
            _probabilityDensity = probabilityDensityFunction;
            BinsCount = binsCount;
            _binWidth = binWidth;
        }

        /// <summary>
        /// Конструктор класса
        /// </summary>
        /// <param name="probabilityDensityFunction">плотность распределения</param>
        /// <param name="histogram">гистограмма</param>
        public ScaledProbabilityDensityFunction(ProbabilityDensityFunction probabilityDensityFunction, IHistogram histogram)
                        	: this( probabilityDensityFunction, histogram.BinsCount, histogram.BinWidth)
        {
        }

        /// <summary>
        /// Возвращает/устанавливает параметры распределения
        /// </summary>
        public double[] Parameters
        {
            get
            {
                double[] result = new double[_probabilityDensity.Parameters.Length + 1];
                _probabilityDensity.Parameters.CopyTo(result, 0);
                result[result.Length - 1] = _binsCount;
                return result;
            }
            set
            {
                _binsCount = value[value.Length - 1];
                _probabilityDensity.Parameters = value;
            }
        }

        /// <summary>
        /// Возвращает/устанавливает число интервалов гистограммы
        /// </summary>
        public double BinsCount
        {
            get
            {
                return _binsCount;
            }
            set 
            { 
                _binsCount = value; 
            }
        }

        /// <summary>
        /// Возвращает как строку
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "Scaled " + _probabilityDensity;
        }

        /// <summary>
        /// Вычисляет значение, основываясь на функции плотности
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public double GetValue(double x)
        {
            return _binsCount * _binWidth * _probabilityDensity.GetValue(x);
        }

        /// <summary>
        /// Возвращает значение распределения и градиент распределения с учетом параметров
        /// </summary>
        /// <param name="x"></param>
        /// <returns>0й элемент - значение распределения, 1й и последующие - градиент распределения</returns>
        public double[] GetValueAndGradient(double x)
        {
            double[] dpg = _probabilityDensity.GetValueAndGradient(x);
            double[] result = new double[dpg.Length + 1];
            double r = _binWidth * _binsCount;
            for (int i = 0; i < dpg.Length; i++)
                result[i] = dpg[i] * r;
            result[dpg.Length] = dpg[0] * _binWidth;
            return result;
        }
    }
}
